package cn.xinfei.xdecision.common.service.script.groovy;

import cn.xinfei.xdecision.common.model.component.scriptrule.RuleTestData;
import cn.xinfei.xdecision.common.model.datax.enums.EnumUtil;
import cn.xinfei.xdecision.common.model.datax.enums.VariableTypeEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.runner.StrUtils;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.EvaluationException;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.Evaluator;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.function.math.Groovy;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.function.math.Python;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ScriptUtils {

    private ScriptUtils() {
    }

    //执行自定义脚本
    public static Object executeScriptBinding(String script, Map<String, RuleTestData> varCodeAndValMap) {

        //替换掉特殊的字符
        script = script.replace("&gt;", ">"); //3&gt;=6 && 3&lt; 12
        script = script.replace("&lt;", "<");
        //$
        script = script.replace("$", "\"");
        //正则匹配自定义中用到的变量对其进行替换
        Pattern pattern = Pattern.compile("@[a-zA-Z0-9_\u4e00-\u9fa5()（）-]+@");
        Matcher matcher = pattern.matcher(script);
        String subexp = script;
        String exp = "";
        int j = 0;
        Map<String, Object> data = new HashMap<>();
        while (matcher.find()) {
            String varCode = matcher.group(0).replace("@", "");
            RuleTestData scriptTestData = varCodeAndValMap.get(varCode);
            if (scriptTestData.getVarValue() == null) {
                return "";
            }
            String v;
            v = "" + varCode;
            data.put(varCode, scriptTestData.getVarValue());
            if (subexp.contains("def main")) {
                // groovy脚本替换为动态参数
                v = "_['" + varCode + "']";
                exp += subexp.substring(j, matcher.end()).replace("@" + varCode + "@", v);
            } else {
                VariableTypeEnum variableTypeEnum = EnumUtil.fromCodeOrThrow(VariableTypeEnum.class, scriptTestData.getValueType());
                if (variableTypeEnum.equals(VariableTypeEnum.INTEGER) || variableTypeEnum.equals(VariableTypeEnum.DOUBLE)) {
                    exp += subexp.substring(j, matcher.end()).replace("@" + varCode + "@", v);
                } else {
                    exp += subexp.substring(j, matcher.end()).replace("@" + varCode + "@", "'" + v + "'");
                }
            }
            j = matcher.end();
        }


        exp += script.substring(j);
        Evaluator evaluator = new Evaluator();
        Object result;
        try {
            if (exp.contains("def main")) {
                // 执行groovy脚本
//                log.debug("groovy:{},{}", exp, data);
                result = Groovy.getInstance().executeForObject(exp, data);
            } else if (exp.contains("def python_main(_):")) {
                //执行python脚本
                result = Python.getInstance().executeForObject(exp, data);
            } else {
                //执行公式
                result = evaluator.evaluate(exp);
            }
            if (result.toString().startsWith("'")) {
                //字符串
                result = result.toString().replace("'", "");
            } else {
                //数值
                if (StrUtils.isNum(result.toString())) {
                    String[] split = result.toString().split("\\.");
                    if (split.length > 1 && StrUtils.strToLong(split[1]) > 0) {
                        result = StrUtils.strToDouble(result.toString());
                    } else {
                        result = StrUtils.strToLong(split[0]);
                    }
                }
            }
        } catch (EvaluationException e) {
            log.error("自定义执行异常", e);
            throw new ApiException(ErrorCodeEnum.RUNNER_CUSTOM_ERROR.getCode(), ErrorCodeEnum.RUNNER_CUSTOM_ERROR.getMessage());
        }
        return result;
    }
}
